#ifndef MUTEX_H
#define MUTEX_H

#include <sys/time.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>

class Mutex
{
public:
	Mutex()
	{
		pthread_mutex_init(&mtx, NULL);
	}
	virtual ~Mutex()
	{
		pthread_mutex_destroy(&mtx);
	}
	inline bool lock()
	{
		pthread_mutex_lock(&mtx);
		return true;
	}

	inline void unlock()
	{
		pthread_mutex_unlock(&mtx);
	}
protected:
	pthread_mutex_t	mtx;
};

class Condition : public Mutex
{
public:
	Condition()
	{
		pthread_cond_init(&m_cond, NULL);
	}
	~Condition()
	{
		pthread_cond_destroy(&m_cond);
	}
	typedef enum {
		WAIT_UNKOWN = -1,
		WAIT_TIMEOUT = 100,
		WAIT_EVENT,
	}WAIT_TYPE;

	int wait(int timeout_ms)
	{
		struct	timeval curtime;
		struct	timespec abstime;
		gettimeofday(&curtime, NULL);
		abstime.tv_sec = curtime.tv_sec + (timeout_ms / 1000);
		abstime.tv_nsec = (curtime.tv_usec + (timeout_ms % 1000) * 1000) * 1000;
		if ( abstime.tv_nsec > 1000000000 )
		{
			abstime.tv_sec += 1;
			abstime.tv_nsec -= 1000000000;
		}
		int retval = -1;
		while(true) {
			retval = pthread_cond_timedwait(&m_cond, &mtx, &abstime);
			if(retval == EINTR)
				continue;
			else if(retval == ETIMEDOUT)
				retval = WAIT_TIMEOUT;
			else if(retval == 0)
				retval = WAIT_EVENT;
			else
				retval = WAIT_UNKOWN;
			break;
		}
		return retval;
	}

	void wait()
	{
		pthread_cond_wait(&m_cond, &mtx);
	}

	bool notify()
	{
		int ret = pthread_cond_signal(&m_cond);
		return ret == 0 ? true : false;
	}

protected:
	pthread_cond_t m_cond;
};

class AutoLock
{
public:
	AutoLock(Mutex *pMutex):m_pMutex(pMutex),m_pCond(NULL)
	{
		m_bSucceed = pMutex->lock();
	}

	AutoLock(Condition *pCond):m_pCond(pCond),m_pMutex(NULL)
	{
		m_bSucceed = pCond->lock();
	}

	virtual ~AutoLock()
	{
		if (m_bSucceed && m_pMutex) {
			m_pMutex->unlock();
		}
		if (m_bSucceed && m_pCond) {
			m_pCond->unlock();
		}
	}
protected:
	Mutex	*m_pMutex;
	Condition *m_pCond;
	bool	m_bSucceed;
};

#endif